/*
 *
 */
package org.jiscinvolve.astrodabis.barberry.common.inport.startable;

import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;

import java.io.IOException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import uk.ac.starlink.table.StarTable;
import uk.ac.starlink.table.ColumnInfo;
import uk.ac.starlink.table.RowSequence;

import org.jiscinvolve.astrodabis.barberry.common.core.Job;
import org.jiscinvolve.astrodabis.barberry.common.core.exception.ProcessingException;

import org.jiscinvolve.astrodabis.barberry.common.data.TapColumn;

/**
 *
 */
public abstract class AbstractStarTableParser
    {
    /**
     * Our debug logger.
     * 
     */
    private static Log log = LogFactory.getLog(AbstractStarTableParser.class);

    /**
     * Protected constructor.
     *
     */
    protected AbstractStarTableParser(Iterator<StarTable> tables)
    throws IOException
        {
        this.tables = tables ;
        }

    /**
     * Our StarTable iterator.
     *
     */
    protected Iterator<StarTable> tables ;

    /**
     * Our current StarTable.
     *
     */
    protected StarTable table ;

    /**
     * Our StarTable row sequence.
     *
     */
    protected RowSequence rowSeq ;

    /**
     * Our current row of data.
     *
     */
    protected Object[] thisRow ;

    /**
     * Our next row of data.
     *
     */
    protected Object[] nextRow ;

    /**
     * Our map of column names.
     *
     */
    protected Map<String,Integer> columns = new HashMap<String,Integer>() ;

    /**
     * Iterator interface method.
     *
     */
    public boolean hasNext()
        {
        if (nextRow != null)
            {
            return true;
            }
        else {
            return tables.hasNext();
            }
        }

    /**
     * Iterator interface method.
     *
     */
    public void remove()
        {
        throw new UnsupportedOperationException(
            "Iterator.remove() not supported"
            );
        }

    /**
     * Process the next table of data.
     *
     */
    protected void nextTable()
    throws ProcessingException
        {
        try {
            //
            // Clear our existing data.
            table   = null ;
            rowSeq  = null ;
            thisRow = null ;
            nextRow = null ;
            columns.clear();
            //
            // Get the next table.
            if (tables.hasNext())
                {
                table = tables.next();
                //
                // Generate our column map.
                int count = table.getColumnCount();
                for (int index = 0 ; index < count ; index++)
                    {
                    columns.put(
                        table.getColumnInfo(index).getName().toLowerCase(),
                        new Integer(
                            index
                            )
                        );            
                    }
                //
                // Get the table RowSequence.
                rowSeq = table.getRowSequence();
                //
                // Get the first row.
                if (rowSeq != null)
                    {
                    if (rowSeq.next())
                        {
                        nextRow = rowSeq.getRow();
                        }
                    }
                }
            }
        catch (IOException ouch)
            {
            log.error("IOException while processing input data [" + ouch.getMessage() + "]");
            throw new ProcessingException(
                "IOException while processing input data",
                ouch
                );
            }
        }

    /**
     * Process the next row of data.
     *
     */
    protected void nextRow()
    throws ProcessingException
        {
        //
        // Find the next non-empty table.
        while ((nextRow == null) && (tables.hasNext()))
            {
            nextTable();
            }
        //
        // Fetch the next row.
        thisRow = nextRow ;
        nextRow = null ;
        try {
            if (rowSeq != null)
                {
                if (rowSeq.next())
                    {
                    nextRow = rowSeq.getRow();
                    }
                }
            }
        catch (IOException ouch)
            {
            log.error("IOException while processing input data [" + ouch.getMessage() + "]");
            throw new ProcessingException(
                "IOException while processing input data",
                ouch
                );
            }
        }

    /*
     * Get a named column index.
     *
     */
    protected int index(String name)
    throws ProcessingException
        {
        //
        // Fix for AstroGrid DSA, outputs column aliases in UPPER CASE.
        Integer index = columns.get(
            name.toLowerCase()
            );
        if (index != null)
            {
            return index.intValue();
            }
        else {
            throw new UnknownColumnException(
                name
                );
            }
        }

    /**
     * Get an Object from the current row.
     *
     */
    protected Object getObject(int i)
    throws ProcessingException
        {
        if (thisRow == null)
            {
            throw new DataIndexException();
            }
        else {
            if (thisRow.length <= i)
                {
                throw new DataIndexException();
                }
            else {
                Object object = thisRow[i] ;
                if (object == null)
                    {
                    return null ;
                    }
                else {
                    return object ;
                    }
                }
            }
        }

    /**
     * Get a String value from the current row.
     *
     */
    protected String getString(TapColumn column)
    throws ProcessingException
        {
        return getString(
            column.name()
            );
        }

    /**
     * Get a String value from the current row.
     *
     */
    protected String getString(String name)
    throws ProcessingException
        {
        return getString(
            index(
                name
                )
            );
        }

    /**
     * Get a String value from the current row.
     *
     */
    protected String getString(int i)
    throws ProcessingException
        {
        Object object = getObject(i) ;
        if (object == null)
            {
            return null ;
            }
        else {
            return object.toString() ;
            }
        }

    /**
     * Get a Double value from the current row.
     *
     */
    protected Double getDouble(TapColumn column)
    throws ProcessingException
        {
        return getDouble(
            column.name()
            );
        }

    /**
     * Get a Double value from the current row.
     *
     */
    protected Double getDouble(String name)
    throws ProcessingException
        {
        return getDouble(
            index(
                name
                )
            );
        }

    /**
     * Get a Double value from the current row.
     *
     */
    protected Double getDouble(int i)
    throws ProcessingException
        {
        Object object = getObject(i) ;
        if (object == null)
            {
            return null ;
            }
        else {
            if (object instanceof Double)
                {
                return (Double) object ;
                }
            /*
             * Promoting Float to Double introduces errors.
            else if (object instanceof Number)
                {
                return ((Number)object).doubleValue() ;
                }
             */
            else {
                String string = object.toString();
                try {
                    return Double.valueOf(
                        string
                        );
                    }
                catch (NumberFormatException ouch)
                    {
                    log.warn("NumberFormatException while processing value [" + string + "]");
                    throw new ProcessingException(
                        ouch
                        );
                    }
                }
            }
        }
    }

